Run the Security plugin under FIPS approved-only mode with BC providers - #5866
Run the Security plugin under FIPS approved-only mode with BC providers#5866beanuwave wants to merge 10 commits into
Conversation
0d1943e to
23bcab1
Compare
23bcab1 to
846a4c3
Compare
846a4c3 to
dd48f9a
Compare
dd48f9a to
dafc362
Compare
PR Reviewer Guide 🔍(Review updated until commit 3ac26d1)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 3ac26d1 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit b5760ff
Suggestions up to commit fd9ed30
Suggestions up to commit 3ab9228
|
|
Persistent review updated to latest commit f908586 |
f908586 to
de757a3
Compare
|
Persistent review updated to latest commit de757a3 |
de757a3 to
b9f7617
Compare
|
Persistent review updated to latest commit b9f7617 |
|
Persistent review updated to latest commit b4fce9b |
b4fce9b to
8e3f614
Compare
|
Persistent review updated to latest commit 8e3f614 |
8e3f614 to
5c719f9
Compare
|
Persistent review updated to latest commit 5c719f9 |
5c719f9 to
b194efc
Compare
|
Persistent review updated to latest commit b194efc |
b194efc to
07f75d1
Compare
|
Persistent review updated to latest commit 07f75d1 |
|
Persistent review updated to latest commit ab97aab |
|
Persistent review updated to latest commit 905dbb1 |
905dbb1 to
b12e4f7
Compare
b12e4f7 to
8f6e1e1
Compare
|
Persistent review updated to latest commit 8f6e1e1 |
| public static final String ADMIN_USER_NAME = "admin"; | ||
| public static final String REGULAR_USER_NAME = "regular_user"; | ||
| public static final String DEFAULT_PASSWORD = "secret"; | ||
| private static final String encryptionKey = Base64.getEncoder().encodeToString("encryptionKey!!".getBytes(StandardCharsets.UTF_8)); |
There was a problem hiding this comment.
We don't care about FIPS compatibility here (as in https://github.com/opensearch-project/security/pull/5866/changes?w=1#diff-f34c84b94aa1e1326e55ce390191ae380ce278f997f4ce1c2849310a662331c4R78)?
There was a problem hiding this comment.
"encryptionKey!!" at 120 bits satisfies cryptographic minimum the provider cares about >= 112bit. But we could reuse TestSecurityConfig.DEFAULT_TEST_PASSWORD here - so it future-proof.
There was a problem hiding this comment.
Would be good to reuse, thanks
| final String requestPath = "/*/_search"; | ||
| final int parallelism = 20; | ||
| final int totalNumberOfRequests = 10_000; | ||
| final int parallelism = FipsMode.isEnabled() ? 8 : 20; |
There was a problem hiding this comment.
This looks odd, why do we have to change these settings? No relation to FIPS I believe
There was a problem hiding this comment.
The generic client fires all requests at once ParallelFlux.from(monos) - for the test with 15k unbounded requests in FIPS-mode (executed in local env) ~80% of exceptions are connect/handshake timeouts at the node: the server can't accept() + handshake fast enough, so SYNs/handshakes expire. FIPS trips it first because the BCTLS handshake is ~x4 costlier.
IMO it comes down to two solutions:
- Explicitly limit parallel requests - this code (introduces in fd9ed30)
- Proper saturation via backpressure (
parallelism= demand). Let Reactor apply backpressure soparallelismis the concurrency limit:
Flux.fromArray(monos).flatMap(mono -> mono, parallelism).collectList().block(...);There was a problem hiding this comment.
What about third option to increase connect timeouts?
There was a problem hiding this comment.
generic-client storm (15k requests, parallelism 100, FIPS)
| Metric | 30s / 30s | 120s / 120s |
|---|---|---|
| ok | 7,920 (52.8%) | 13,525 (90.2%) |
| failed | 7,080 | 1,475 |
TCP_CONNECT_TIMEOUT |
5,144 | 0 |
TLS_HANDSHAKE_TIMEOUT |
839 | 0 |
TCP_CONNECTION_RESET |
738 | 1,281 |
PREMATURE_CLOSE |
359 | 194 |
| elapsed time | 50.1s | 81.9s |
Takeaway: raising the connect/handshake timeout (30s -> 120s) eliminates the*_TIMEOUT failures but cannot make the run green - TCP_CONNECTION_RESET (accept-queue overflow) grows and wall-clock rises.
2nd option is a better solution, because it keeps the accept queue shallow so nothing overflows and nothing waits near a timeout.
There was a problem hiding this comment.
I see, thank you, let's do 2nd option
| if (protocol == HttpProtocol.HTTP11) { | ||
| Consumer<SslContextBuilder> http11Configure = s -> { | ||
| if (FipsMode.isEnabled()) { | ||
| s.sslProvider(SslProvider.JDK); |
There was a problem hiding this comment.
I believe we used to have openssl tests (and dependencies) but not anymore, how come non-JDK provider could be selected? We shouldn't have any on classpath
There was a problem hiding this comment.
true, it's dead code on arrival. We can revert this change or replace it with assert !OpenSsl.isAvailable()
|
|
||
| private static String fipsCompatibleEncryptionKey() { | ||
| final byte[] keyMaterial = new byte[32]; | ||
| new SecureRandom().nextBytes(keyMaterial); |
There was a problem hiding this comment.
| new SecureRandom().nextBytes(keyMaterial); | |
| Randomness.createSecure().nextBytes(keyMaterial) | |
| ```? |
|
|
||
| static { | ||
| byte[] bytes = new byte[16]; // satisfies BC FIPS 112-bit minimum | ||
| new SecureRandom().nextBytes(bytes); |
There was a problem hiding this comment.
| new SecureRandom().nextBytes(bytes); | |
| Randomness.createSecure().nextBytes(bytes); | |
| ```? |
| * <p>This is necessary because JNDI LDAP resolves hostnames to IP addresses before creating | ||
| * SSL sockets, making the hostname unavailable for SNI configuration. | ||
| */ | ||
| public class HostnameAwareConnectionFactory extends DefaultConnectionFactory { |
There was a problem hiding this comment.
The LDAP related changes (SNI, etc) seems to be unrelated to FIPS. could we extract them into separate feature + pull request?
There was a problem hiding this comment.
I wouldn't quite call the LDAP changes FIPS-unrelated. The SNI plumbing and the PKCS12 workaround only become necessary because of running LDAPS through the BCTLS/BCFIPS provider stack.
That said, I do agree they're a fairly substantial, self-contained part of this PR. Splitting them out would make this diff smaller and keep LDAP-related chunk in one place.
Btw. as mentioned in the PR description, updating ldaptive for version 2.x would likely make the SNI plumbing unnecessary anyway.
There was a problem hiding this comment.
Thanks @beanuwave
That said, I do agree they're a fairly substantial, self-contained part of this PR. Splitting them out would make this diff smaller and keep LDAP-related chunk in one place.
👍 , this is exactly the reasons
Btw. as mentioned in the PR description, updating ldaptive for version 2.x would likely make the SNI plumbing unnecessary anyway.
If we could do that (as separate change) so we could get rid of LDAP workarounds - even better
There was a problem hiding this comment.
sound like a plan - I'll prepare a new LDAP PR!
| * @return true when the key material is held in a PKCS#11 token. Such keys are non-exportable, so the | ||
| * TLS engine must delegate signing to the token's provider (SunPKCS11) rather than BouncyCastle FIPS. | ||
| */ | ||
| default boolean isPkcs11() { |
There was a problem hiding this comment.
Could we instead of checking PKCS11 in many places as true/false, represent that as data classes? For Pkcs11KeyStoreConfiguration and BcfksKeyStoreConfiguration fe?
There was a problem hiding this comment.
Certainly, this is a much cleaner design! Originally I rushed to get this through with a low-footprint design. However using polymorphism introduces quite a few code changes. Would you prefer the TrustStoreConfiguration implementations to remain as inner classes?
There was a problem hiding this comment.
Thanks @beanuwave
Would you prefer the TrustStoreConfiguration implementations to remain as inner classes?
Yeah, I think we could keep it there (and may be make them sealed if that makes sense). thanks!
|
@beanuwave this is tremendous effort, thank you, a few high level comments:
Hope it make sense, thank you! |
|
@reta Thank you for pushing this through the review process - that's great! If you plan to do a second review, I'd really appreciate it if you could also take the "Reviewer call-outs" into account.
The FIPS test class abstraction is a no-brainer, since it would then follow the same style as the core project. However, I'm not sure the same general rules can be applied to production code - it's more of a case-by-case assessment.
Do you have a specific scenario in mind where a heterogeneous cluster would be advantageous and provide real-world value? Assuming it would be possible for a non-FIPS node to join a FIPS cluster, IMO that would introduce a weak link in the chain and undermine the FIPS security guarantees of the entire cluster. |
👍
It is probably could be summarized like that: how would we recommend to start with FIPS support. Only brand new clusters or gradual migrations.
Correct, tests are indeed no-brainer, the one which really clicked for me for PCKS11, I think we could have cleaner implementation, but certainly - case by case. |
|
Persistent review updated to latest commit b5760ff |
…th a StorePassword wrapper Signed-off-by: Iwan Igonin <iigonin@sternad.de> Co-authored-by: Benny Goerzig <benny.goerzig@sap.com> Co-authored-by: Karsten Schnitter <k.schnitter@sap.com> Co-authored-by: Kai Sternad <k.sternad@sternad.de>
b5760ff to
3ac26d1
Compare
|
Persistent review updated to latest commit 3ac26d1 |
| import org.opensearch.security.support.PemKeyReader; | ||
|
|
||
| public interface KeyStoreConfiguration { | ||
| public sealed interface KeyStoreConfiguration { |
| .endpointIdentificationAlgorithm(null) | ||
| .build(); | ||
| .endpointIdentificationAlgorithm(null); | ||
| routePkcs11ThroughSunJsse(builder); |
There was a problem hiding this comment.
May be add a method configure to KeyStoreConfiguration (noop for JDK, routePkcs11ThroughSunJsse for PKCS11): keyStoreConfiguration.configure(SslContextBuilder builder)
| final var sslConfigSettings = settings.getByPrefix(fullSslConfigSuffix); | ||
| if (settings.hasValue(sslConfigSuffix + KEYSTORE_FILEPATH)) { | ||
| final var keyStorePassword = resolvePassword(sslConfigSuffix + KEYSTORE_PASSWORD, settings, DEFAULT_STORE_PASSWORD); | ||
| final boolean isPkcs11Keystore = PemKeyReader.PKCS11.equalsIgnoreCase(environment.settings().get(sslConfigSuffix + KEYSTORE_TYPE)); |
There was a problem hiding this comment.
Could we moved those to factory methods? TrustStoreConfiguration::buildTrustStoreConfiguration and KeyStoreConfiguration::buildKeyStoreConfiguration ?
Description
Category: Enhancement, New feature, Bug fix
This branch makes the Security plugin run correctly under FIPS approved-only mode (BCFIPS as the sole crypto provider). Every change traces to one of: an algorithm/format/usage that FIPS disallows (whether or not BCFIPS rejects it at runtime), a weakness surfaced during the audit, or build/packaging plumbing to make FIPS the always-available baseline.
At a glance:
FipsMode.isEnabled()(envOPENSEARCH_FIPS_MODE=true) replacesCryptoServicesRegistrar.isInApprovedOnlyMode()everywhere. Intent is decoupled from provider state and cross-checked at startup.compileOnly, shipped by core); the compile-timeFipsBuildParamsfork is gone. FIPS engages purely through thejava.securitythe JVM loads (no provider is registered in code) - the runtime launcher merges infips_java.securityonOPENSEARCH_FIPS_MODE=true, while tests swap it wholesale (-D...properties==<file>).PemKeyReaderrebuilt on BouncyCastle; TLSv1.1 dropped in FIPS mode.securityadminaccepts BCFKS/PKCS11 and now launches via core'sopensearch-cli.Key changes
OBO / JWT tokens
The OBO key path was audited end-to-end; the encryption rewrite is the FIPS trigger, the rest are weaknesses found alongside it.
EncryptionDecryptionUtil: built its cipher with the bareCipher.getInstance("AES")and a key forced to 16 bytes viaArrays.copyOf(silently truncating or zero-padding) -> now AES-256-GCM (random 12-byte IV, 128-bit tag), key derived via HKDF-SHA256. A bare"AES"transformation resolves to the JCE provider's default mode - ECB - which for data confidentiality violates NIST SP 800-38A2 (ECB is approved only for key wrapping, SP 800-38F3). The mode is never spelled out or configurable, and BC FIPS permits the ECB primitive at runtime - so this had to be caught by review, not the provider.encryption_keywould produce a nominal AES-256 key with sub-112-bit strength. A FIPS guard rejects input keying material < 32 bytes and zeroes the IKM after derivation.OnBehalfOfAuthenticatorinitializes lazily and atomically: the constructor is inert, the first token request triggers init, a failure logs once and declines - a bad key can't throw out of the constructor, propagate through config reload, and retry forever. MirrorsApiTokenAuthenticator.KeyUtils.loadKeyFromKeystore) instead of inline config, keeping the secrets out of cluster state. Relative*_keystore_pathvalues resolve against the node config dir (consistent with other security file settings). This is an SP 800-576 key-at-rest hardening (protecting keying material / limiting exposure), not a FIPS 140-31 requirement.OnBehalfOfSettings.toString()redacts thesigning_key/encryption_keyvalues so they no longer leak into logs.Keystores / TLS
PemKeyReaderrewritten onto BouncyCastle (PEMParser/JcaPEMKeyConverter/ PKCS8 decryptor) instead of raw JCE; adds BCFKS + PKCS11 and store-type auto-detection.SSLConfigConstants: both defaults are now FIPS-conditional - default store type is forced to BCFKS in FIPS, andALLOWED_SSL_PROTOCOLSdrops TLSv1.1 in FIPS. Both LDAP backends now reuseALLOWED_SSL_PROTOCOLSfor their defaultenabled_ssl_protocols.no encoding for key); SunJSSE delegates the handshake signature to the key's own provider (SunPKCS11). So when the keystore is PKCS#11,SslConfigurationbuilds theSSLContextagainst SunJSSE (SslContextBuilder.sslContextProvider(SunJSSE)).mainself-registered it at plugin load (OpenSearchSecuritySSLPlugin.tryAddSecurityProvider()->Security.addProvider(new BouncyCastleFipsProvider())); that method is removed. Providers now come solely from the activejava.securityfile (JCA lazy-loads them), so FIPS vs non-FIPS is a launch-time provider swap (BCJSSE vs SunJSSE) with no code branch - the security files are a core/distribution concern.LDAP
SNISettingTLSSocketFactorysets the ClientHello SNI before the handshake so a multi-cert server serves the right cert;HostnameVerifyingTrustManagerchecks the returned cert after. For IP targets the SNI factory early-returns (no SNI) and the trust manager is the only hostname check; for DNS it sets SNI + endpointIdentification (the trust-manager hostname check is then redundant).HostnameAwareConnectionFactorythreads the target hostname through so SNI works.create*CredentialConfigwith key aliases routes throughKeyStoreSSLContextInitializer.getKeyManagers(), which copies the private key into a fresh in-memory PKCS12 store; SunJCE protects it withPBEWithHmacSHA256AndAES_256, not available in FIPS. Fix: build BCFKS keystores from PEM viaPemKeyReader.toTruststore/toKeystoreand pass null key aliases, sokmf.init(keystore, password)is called directly and the PKCS12-copy branch is bypassed. (LDAPAuthorizationBackendpreviously usedcreateX509CredentialConfig, which hit the same path unconditionally.)Java9CLinto a sharedSocketFactoryClassLoader(resolves the JNDI socket-factory classes by name) and set it onldap2's JNDI provider config, soPrivilegedProvider's thread-context swap resolves the socket factory on reconnect. Fixes an ldap2 LDAPS reconnectClassNotFoundException.Auth hardening (found mid-audit)
HTTPSpnegoAuthenticator: no longer mutates globalSystem.setPropertydebug flags; stops logging the acceptor principal; properLoginContext.logout()+ decoded-header zeroing infinally.InternalAuthenticationBackend+PasswordHasher.getDummyHash(): not-found timing path now uses the configured hasher (closes user-enumeration side-channel under PBKDF2).PasswordValidator.FIPS_MIN_PASSWORD_LENGTH(14) now anchors both ends: FIPS raises an unsetrestapi.password_min_lengthto 14, and startup rejects a lower explicit value - otherwise the REST API accepts passwords the hasher then refuses.Randomness.createSecure()replacesnew SecureRandom()for OBO encryption, api-tokens, and user passwords - it resolves to the approved SP 800-90A4 DRBG in FIPS.UserServicegenerates 20-27 chars in FIPS (>=119 bits over the 62-char alphabet), 8-15 otherwise;char[]zeroed infinally.CLI
SecurityAdmin: accepts BCFKS/PKCS11; PKCS#11 PIN prompt. Launcher scripts now delegate to core's sharedopensearch-cli. Standalone bundle ships BCFIPS jars underdeps/.SecurityAdmin.buildPkcs11SslContextroutes PKCS#11 keys through SunJSSE client-side (same as the server TLS layer - see Keystores/TLS).Note
Reviewer call-outs
securityadminnow launches via core'sopensearch-cli- this targets the in-distribution path; the standalone bundle is no longer self-launching and is effectively deprecated.System.setProperty(disableEndpointIdentification, true)from the defaultldapbackend - now warn-only. Two reasons: (a) it aligns both backends on the same logic (ldap2never set it); (b) mutating a global JVM property from application code is bad practice - it's process-wide and load-order-dependent, so one auth-domain'sverify_hostnames: falsesilently reconfigured hostname checking for the whole JVM. Operators who need it must now set the-Dflag deliberately.verify_hostnamesandtrust_allcoupled to the same verifier? -verifyHostnames = !trustAll && <setting>, sotrust_all: trueforcesAllowAnyHostnameVerifieron top ofAllowAnyTrustManager. Chain validation and hostname matching are orthogonal concerns; collapsing them onto one code path means you can't relax one without the other and hides which layer a config change actually touches. Worth untangling so each parameter maps to exactly one verification layer.Core / distribution follow-ups
Not plugin changes - each fix belongs in core or the distribution, tracked here so it can be routed:
bctls-fipshas no socketconnectgrant. Core's basesecurity.policygrantsbc-fips/bcpkix-fipsbut notbctls-fips, so under the agent all outbound BCJSSE TLS (LDAPS, audit sinks, remote reindex/snapshot over https) is denied - and plugin policies can't grant a corelib/jar. Verified against a live LDAPS-over-FIPS cluster.LogConfiguratorsetsjava.util.logging.managerat runtime, too late for the BouncyCastle FIPS JSSE provider (which initialises JUL during bootstrap); its per-handshake INFO traces then leak toSystem.erras[WARN][stderr]spam. Fix in the distribution: set-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManageras a launch-time-Dinjvm.options, and defaultorg.bouncycastle.jssetowarnin the shippedlog4j2.properties.org.opensearch.fips.FipsModestill detects FIPS viaCryptoServicesRegistrar.isInApprovedOnlyMode()- the provider-state probe this branch replaced with the env var; it should follow.BCFipsEntropyDaemonFiltershould move into core's test framework. The local thread-filter accommodation for the "BC FIPS Entropy Daemon" thread belongs in core'sBouncyCastleThreadFilter, not this plugin's test sources.Additional notes
SecretKeyentries (engineSetKeyEntryrequiresPrivateKey) - relevant to the JWT-signing-key path; BCFKS is the only FIPS-approved store that holds secret keys.SunJGSSis deliberately retained for Kerberos/SPNEGO.SNISettingTLSSocketFactory+ theSniAwareConnectiondecorator + hostname ThreadLocal +HostnameAwareConnectionFactory) works around the JNDI LDAP provider resolving hostnames to IPs before socket creation (bcgit/bc-java#460); ldaptive 2.x's native (Netty) transport opens sockets with the real hostname, which would let this entire stack be deleted (out of scope here).Testing
The suite runs in non-FIPS mode by default. To exercise the FIPS code paths, set the environment variable before invoking Gradle:
OPENSEARCH_FIPS_MODE=true ./gradlew test integrationTestWhen set, the build swaps in the FIPS
java.securitypolicy (BCFIPS-only providers), enables-Dorg.bouncycastle.fips.approved_only=true, and points the JVM at the BCFKS truststore. FIPS-incompatible tests (BCrypt, Argon2, SAML, SSLv3, JKS/PKCS12, weak/short passwords) are auto-skipped via JUnit assumptions. Static bcrypt fixtures and their short demo passwords are rewritten to PBKDF2 and padded past the 14-char floor byFipsHashAdapter(a no-op outside FIPS), and a few timing-sensitive integ tests scale down under FIPS, where PBKDF2 logins and BCTLS handshakes are markedly slower.For a running cluster, select the FIPS-approved password hasher in
opensearch.yml(BCrypt/Argon2 are not available in approved-only mode):The demo hashes in
config/opensearch-security/internal_users.ymlare BCrypt, which won't verify under PBKDF2 - regenerate the hash for each test account (e.g. withtools/hash.sh) and replace it before applying the security config.Test securityadmin.sh with BCFKS + PKCS#11 keystores (SoftHSM)
Exercises a token-resident node TLS key (signed via SunJSSE) and
securityadminauthenticating with a PKCS#11 client key. This example is FIPS-specific, but adjusts easily to non-FIPS by registering a SunPKCS11 provider viaOPENSEARCH_JAVA_OPTS="-Djava.security.properties=$OPENSEARCH_HOME/config/java.security"instead ofOPENSEARCH_FIPS_MODE=true. All paths below are relative to$OPENSEARCH_HOME.Test LDAP authentication over LDAPS (SNI, hostname verification, mTLS)
Self-contained manual tests for the LDAP TLS changes: SNI / hostname verification, mutual TLS, and the TLS protocol floor. Authentication only - these changes don't touch authz code, so role resolution is out of scope (verify that against a real directory).
Run them against any LDAPS directory that supports mTLS (a client cert is required). The walkthrough uses a local UnboundID in-memory stand-in only because it's repeatable and trivial to set up - a convenience, not a requirement; substitute your own server anywhere it appears. Its setup lives in
LDAP_UNBOUNDID_STANDIN_GUIDE.md.TLS material is the OpenSearch install's own demo certs in
$OPENSEARCH_HOME/config/-esnode(server, SAN includeslocalhost),root-ca.pem(trust anchor),kirk(client). Run the node withOPENSEARCH_FIPS_MODE=true(omit for non-FIPS; the only observable difference is the TLS protocol floor).Test matrix. Run every scenario in all four cells - flip the backend on
type:; for FIPS setOPENSEARCH_FIPS_MODE=true(launcher loadsfips_java.security-> BCJSSE), for non-FIPS setOPENSEARCH_JAVA_OPTS="-Djava.security.properties=$OPENSEARCH_HOME/config/java.security"(BCFIPS stays declared - the FIPS installer converts the node stores to BCFKS - but TLS runs on SunJSSE). Launch-time provider swap, no code path (see Keystores / TLS). Outcomes are identical; only the protocol floor ([TLSv1.3, TLSv1.2]FIPS vs+ TLSv1.1non-FIPS) and the provider differ, so theProv*/TlsFatalAlertclass names in the excerpts are BCJSSE-only.ldap...ldap2.LDAPAuthenticationBackend2)Scenario 1 - hostname verification. Same trusted
esnodecert throughout; 1b-1d dial a name not in its SAN (echo "127.0.0.1 ldap-wrong.example.com" | sudo tee -a /etc/hosts, then sethosts: [ldap-wrong.example.com:8636]), so the only thing that can object is one of the two hostname guards: (1) ldaptive's verifier (verify_hostnames), (2) JNDI endpoint-id (-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=trueinconfig/jvm.options; the plugin no longer sets it, only warns). Chain trust is valid throughout, so this isolates hostname checking - the untrusted-cert case is 2c. Apply + restart per row.verify_hostnamestruetrueDefaultHostnameVerifierfalseverify_hostnames: falsealone isn't enough)falseCleanup: remove the
/etc/hostsline + thejvm.optionsflag, restorehosts:+verify_hostnames: true. Never setdisableEndpointIdentification=truein production (it's process-wide).Scenario 2 - mTLS client authentication. Vary only the client cert / trust anchor; apply + restart per row, and restore
pemtrustedcas_filepath: root-ca.pemafter 2c. First generate the two "bad" credentials once - both self-signed, so neither chains to the demoroot-ca- intoconfig/:config.yml)pemcert/pemkey_filepath: kirk.*(baseline)pemcert/pemkey_filepath: untrusted-client.*pemtrustedcas_filepath: untrusted-ca.pemTest OnBehalfOf (OBO) token
Exercises OBO token issuance and verification against an already-running cluster, in three modes: keys inline in the dynamic config (Scenario A), held in a BCFKS keystore out of cluster state (Scenario B), or in a PKCS#12 keystore for non-FIPS builds (Scenario C). No restart needed -
-t configpushes only the dynamicon_behalf_ofblock, picked up live. Pick ONE scenario, editconfig.yml, then run the apply / issue / use steps. All paths are relative to$OPENSEARCH_HOME.Issues Resolved
Resolves RFC
Related to the series of FIPS PRs in the security plugin:
Check List
References